package com.grakol.parallel.samples.tests;

import static org.junit.Assert.assertTrue;

import org.junit.Before;
import org.junit.Test;

import com.grakol.parallel.samples.IFunction;
import com.grakol.parallel.samples.MultiTaskLauncher;

/**
 * Runs Fibonacci test with 1 thread and 2 threads to compare results. 
 * 
 * @author LK
 *
 */
public class TestFibonacci {
	private IFunction<Long> rcrFunction;
	private IFunction<Long> itrFunction;
	
	@Before
	public void setUp() {
		rcrFunction = new FibonacciFunction( 30 );
		itrFunction = new FibonacciFunction( 30, true );
	}

	@Test
	public void runFunction() {
		rcrFunction.perform();
		assertTrue( rcrFunction.getResult().equals( 832040L ) );
		itrFunction.perform();
		assertTrue( itrFunction.getResult().equals( 832040L ) );
	}
	
	/**
	 * @param args input_number
	 */
	public static void main(String[] args) {
		if( args.length < 1 ) {
			System.out.println( "usage: TestFibonacci input_number" );
			return;
		}
		long inputNo = Long.valueOf( args[0] );
		// test using current thread
		IFunction<Long> function = new FibonacciFunction( inputNo );
		// measure execution time
		long startTime = System.nanoTime();
		function.perform();
		long result = function.getResult();
		long timeElapsedMs = (System.nanoTime() - startTime)/1000000;
		// print result for 1 thread
		System.out.println( "Single thread:   Fib(" + inputNo + ") = " + result + " [" + timeElapsedMs + " ms]" );
		// test using MultiTaskLauncher with 2 threads
		FibonacciFunction[] functions = new FibonacciFunction[2];
		functions[0] = new FibonacciFunction( inputNo - 1 );
		functions[1] = new FibonacciFunction( inputNo - 2 );
		MultiTaskLauncher<Long> launcher = new MultiTaskLauncher<Long>( functions  );
		// measure execution time
		startTime = System.nanoTime();
		launcher.start();
		result = launcher.getMergedResult();
		timeElapsedMs = (System.nanoTime() - startTime)/1000000;
		// print result for 2 threads
		System.out.println( "Two threads:     Fib(" + inputNo + ") = " + result + " [" + timeElapsedMs + " ms]" );
		launcher.finish();
		// test using 2 threads and performance optimization 
		// F(n) = F(m+1)F(n-m)+F(m)F(n-m-1), m < n;
//		long perfThreshold = 35; // choose for optimal performance
//		long m = inputNo - perfThreshold;
//		if( m < 1 )
//			return; // optimization does not make sense
//		functions[0] = new FibonacciFunction( inputNo - m );
//		functions[1] = new FibonacciFunction( inputNo - m - 1 );
//		FibonacciFunction[] mFunctions = new FibonacciFunction[2];
//		mFunctions[0] = new FibonacciFunction( m + 1 );
//		mFunctions[1] = new FibonacciFunction( m );
//		launcher = new MultiTaskLauncher<Long>( functions  );
//		// measure execution time
//		startTime = System.nanoTime();
//		mFunctions[0].perform();
//		mFunctions[1].perform();
//		launcher.start();
//		List<Long> results = launcher.getResults();
//		result = mFunctions[0].getResult()*results.get( 0 ) + mFunctions[1].getResult()*results.get( 1 );
//		timeElapsedMs = (System.nanoTime() - startTime)/1000000;
//		System.out.println( "Two threads(" + perfThreshold + "): Fib(" + inputNo + ") = " + result + " [" + timeElapsedMs + " ms]" );
//		launcher.finish();
	}

}
